﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Linq;
using LoggerLib;
using Microsoft.Xrm.Client;
using Microsoft.Xrm.Client.Services;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using Crm2015SdkExtensions;
using Microsoft.Xrm.Sdk.Metadata;

namespace FetchXmlTool
{
    public partial class FetchXmlEvaluator : Form
    {
        private CrmConnection _connection;
        private OrganizationService _organizationService;
        private string _fetchXml;
        private DataTable _dataTable = new DataTable();
        private ConnectionsForm connectionsForm = new ConnectionsForm();
        private int _pageNumber = 1;
        private int _pageRecordCount = 150;
        private string _pagingCookie = null;
        private bool _moreRecords = false;
        private int _totalRecordCount;
        private bool _isFileDirty = false;

        public List<ConnectionStringSettings> connectionStringSettingsList;

        public FetchXmlEvaluator()
        {
            InitializeComponent();

            dataGridViewResult.RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders;
            dataGridViewResult.CellFormatting += dataGridViewResult_CellFormatting;
            dataGridViewResult.CellContentDoubleClick += dataGridViewResult_CellContentDoubleClick;

            try
            {
                connectionsForm.FormClosed += connectionsForm_FormClosed;

                connectionStringSettingsList = new List<ConnectionStringSettings>(ConfigurationManager.ConnectionStrings.Cast<ConnectionStringSettings>());
                RefreshConnectionStringSettings();

                string connectionString = comboBoxConnectionString.SelectedValue.ToString();
                toolStripStatusLabel1.Text = connectionString;
                Initialize(connectionString);

                newToolStripMenuItem_Click(null, new EventArgs());
            }
            catch (Exception ex)
            {
                FillTextBox(ex.ToString());
            }
        }

        private void Initialize(string connectionString)
        {
            try
            {
                Logger.Initialize();
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                _connection = CrmConnection.Parse(connectionString);
                _organizationService = new OrganizationService(_connection);
            }
        }

        void connectionsForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            RefreshConnectionStringSettings();
        }

        private void RefreshConnectionStringSettings()
        {
            comboBoxConnectionString.DataSource = null;
            comboBoxConnectionString.DataSource = connectionStringSettingsList;
            comboBoxConnectionString.DisplayMember = "Name";
            comboBoxConnectionString.ValueMember = "ConnectionString";
            comboBoxConnectionString.SelectedItem = connectionStringSettingsList[connectionStringSettingsList.Count - 1];
        }

        protected override void OnClosing(CancelEventArgs e)
        {
            base.OnClosing(e);

            if (_isFileDirty)
            {
                // TODO jak na dialog?
                //CommonDialog dialog = new CommonDialog();
                //dialog.Container.Add(new Button(), )
            }
        }

        private int GetPageRecordCount()
        {
            int pageRecordCount;
            if (!Int32.TryParse(textBoxRowsPerPage.Text, out pageRecordCount))
                pageRecordCount = 150;

            if (pageRecordCount <= 0)
                pageRecordCount = 1;
            else if (pageRecordCount > 5000)
                pageRecordCount = 5000;

            textBoxRowsPerPage.Text = pageRecordCount.ToString();

            return pageRecordCount;
        }

        private void buttonEvaluate_Click(object sender, EventArgs e)
        {
            if (backgroundWorkerServer.IsBusy)
                return;

            OnButtonClick();
            labelResultCount.Visible = false;
            buttonClr_Click(sender, e);

            _fetchXml = PreProcessFetchXml(fastColoredTextBoxFetchXml.Text);
            _pageRecordCount = GetPageRecordCount();
            _pageNumber = 1;
            _pagingCookie = null;

            Tuple<string, int, int, string> argument = new Tuple<string, int, int, string>(_fetchXml, _pageRecordCount, _pageNumber, _pagingCookie);
            backgroundWorkerServer.RunWorkerAsync(argument);
        }

        private void buttonPreviousPage_Click(object sender, EventArgs e)
        {
            if (backgroundWorkerServer.IsBusy)
                return;

            OnButtonClick();

            _pageNumber--;

            Tuple<string, int, int, string> argument = new Tuple<string, int, int, string>(_fetchXml, _pageRecordCount, _pageNumber, _pagingCookie);
            backgroundWorkerServer.RunWorkerAsync(argument);
        }

        private void buttonNextPage_Click(object sender, EventArgs e)
        {
            if (backgroundWorkerServer.IsBusy)
                return;

            OnButtonClick();

            _pageNumber++;

            Tuple<string, int, int, string> argument = new Tuple<string, int, int, string>(_fetchXml, _pageRecordCount, _pageNumber, _pagingCookie);
            backgroundWorkerServer.RunWorkerAsync(argument);
        }

        private void buttonFirstPage_Click(object sender, EventArgs e)
        {
            if (backgroundWorkerServer.IsBusy)
                return;

            OnButtonClick();

            _pageNumber = 1;

            Tuple<string, int, int, string> argument = new Tuple<string, int, int, string>(_fetchXml, _pageRecordCount, _pageNumber, _pagingCookie);
            backgroundWorkerServer.RunWorkerAsync(argument);
        }

        private void buttonLastPage_Click(object sender, EventArgs e)
        {
            if (backgroundWorkerServer.IsBusy)
                return;

            OnButtonClick();

            _pageNumber = (_totalRecordCount % _pageRecordCount == 0) ? (int)(_totalRecordCount / _pageRecordCount) : (int)(_totalRecordCount / _pageRecordCount) + 1;

            Tuple<string, int, int, string> argument = new Tuple<string, int, int, string>(_fetchXml, _pageRecordCount, _pageNumber, _pagingCookie);
            backgroundWorkerServer.RunWorkerAsync(argument);
        }

        private void buttonClr_Click(object sender, EventArgs e)
        {
            textBoxInfo.Text = String.Empty;
        }

        private void OnButtonClick()
        {
            buttonEvaluate.Enabled = false;
            buttonPreviousPage.Enabled = false;
            buttonNextPage.Enabled = false;
            buttonFirstPage.Enabled = false;
            buttonLastPage.Enabled = false;

            noresultLabel.Visible = false;
            elapsedTimeLabel.Text = String.Empty;

            _dataTable.Rows.Clear();
            _dataTable.Columns.Clear();

            toolStripStatusLabel2.Text = "retrieving data from server";
            toolStripProgressBar1.Increment(10);
        }

        private string PreProcessFetchXml(string fetchXml)
        {
            string key, value;
            bool use;

            foreach (DataGridViewRow row in dataGridViewReplacements.Rows)
            {
                use = row.Cells["Use"].Value != null ? (bool)row.Cells["Use"].Value : false;
                if (!use)
                    continue;

                key = row.Cells["Key"].Value != null ? row.Cells["Key"].Value.ToString() : null;
                value = row.Cells["Value"].Value != null ? row.Cells["Value"].Value.ToString() : String.Empty;

                if (!String.IsNullOrWhiteSpace(key))
                    fetchXml = fetchXml.Replace(key, value);
            }

            return fetchXml;
        }

        private void FillTextBox(string text)
        {
            textBoxInfo.Text = textBoxInfo.Text
                + "==============================================================================="
                + Environment.NewLine
                + Environment.NewLine
                + text
                + Environment.NewLine
                + Environment.NewLine;
        }

        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            base.OnFormClosing(e);

            if (_organizationService != null)
            {
                try
                {
                    _organizationService.Dispose();
                }
                catch (Exception ex)
                {
                    Logger.AddWarning("FetchXmlTool.FetchXmlEvaluator", ex);
                }
                finally
                {
                    _organizationService = null;
                }
            }
        }

        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                OpenFileDialog openFileDialog = new OpenFileDialog();
                //openFileDialog.DefaultExt = "Text Files|*.txt|Xml Files|*.xml|All Files|*.*";

                string initialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
                string value = ConfigurationManager.AppSettings.Get("defaultDirectory");
                if (!String.IsNullOrEmpty(value) && Directory.Exists(value))
                    initialDirectory = value;

                openFileDialog.InitialDirectory = initialDirectory;

                DialogResult result = openFileDialog.ShowDialog();
                if (result == DialogResult.OK)
                {
                    string fileDirectory = Path.GetDirectoryName(openFileDialog.FileName);
                    if (fileDirectory != initialDirectory)
                    {
                        Configuration configuration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
                        if (configuration != null)
                        {
                            configuration.AppSettings.Settings.Add("defaultDirectory", fileDirectory);
                            configuration.Save();
                        }
                    }

                    StringBuilder fetchXml = new StringBuilder();
                    List<Tuple<string, string, string, string>> keyValuePairs = new List<Tuple<string, string, string, string>>();

                    using (Stream stream = openFileDialog.OpenFile())
                    {
                        using (XmlTextReader xmlTextReader = new XmlTextReader(stream))
                        {
                            xmlTextReader.ReadToFollowing("fetch");

                            XElement fetch = XElement.Parse(xmlTextReader.ReadOuterXml());

                            XmlWriterSettings xmlWriterSettings = new XmlWriterSettings();
                            xmlWriterSettings.OmitXmlDeclaration = true;
                            xmlWriterSettings.Indent = true;
                            xmlWriterSettings.IndentChars = "    ";

                            using (XmlWriter xmlTextWriter = XmlTextWriter.Create(fetchXml, xmlWriterSettings))
                                fetch.Save(xmlTextWriter);

                            xmlTextReader.ReadToFollowing("keyValuePair");
                            while (xmlTextReader.Name == "keyValuePair")
                            {
                                keyValuePairs.Add(new Tuple<string, string, string, string>(
                                    xmlTextReader.GetAttribute("use"),
                                    xmlTextReader.GetAttribute("key"),
                                    xmlTextReader.GetAttribute("value"),
                                    xmlTextReader.GetAttribute("note")));

                                xmlTextReader.ReadToFollowing("keyValuePair");
                            }
                        }
                    }

                    fastColoredTextBoxFetchXml.Text = fetchXml.ToString();
                    fastColoredTextBoxFetchXml.Tag = openFileDialog.FileName;
                    fastColoredTextBoxFetchXml.TextChanged += fastColoredTextBoxFetchXml_TextChanged;
                    _isFileDirty = false;
                    labelFileName.Text = String.Format("[{0}]", fastColoredTextBoxFetchXml.Tag);

                    dataGridViewReplacements.Rows.Clear();
                    foreach (var item in keyValuePairs)
                    {
                        bool use;
                        Boolean.TryParse(item.Item1, out use);

                        dataGridViewReplacements.Rows.Add(use, item.Item2, item.Item3, item.Item4);
                    }

                    dataGridViewReplacements.CellValueChanged += dataGridViewReplacements_CellValueChanged;
                }
            }
            catch (Exception ex)
            {
                FillTextBox(ex.ToString());
            }
        }

        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                SaveFileDialog saveFileDialog = new SaveFileDialog();
                saveFileDialog.DefaultExt = "xml";
                saveFileDialog.AddExtension = true;
                string documentsFolder = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
                string documentsFetchFolder = Path.Combine(documentsFolder, "FetchXmls");
                string folder = Directory.Exists(documentsFetchFolder) ? documentsFetchFolder : documentsFolder;
                if (!String.IsNullOrWhiteSpace(folder))
                    saveFileDialog.InitialDirectory = folder;
                if (fastColoredTextBoxFetchXml.Tag != null)
                    saveFileDialog.FileName = (string)fastColoredTextBoxFetchXml.Tag;

                DialogResult result = saveFileDialog.ShowDialog();
                if (result == System.Windows.Forms.DialogResult.OK)
                {
                    using (Stream stream = saveFileDialog.OpenFile())
                        SaveXmlFile(stream);

                    fastColoredTextBoxFetchXml.Tag = saveFileDialog.FileName;
                    fastColoredTextBoxFetchXml.TextChanged += fastColoredTextBoxFetchXml_TextChanged;
                    dataGridViewReplacements.CellValueChanged += dataGridViewReplacements_CellValueChanged;
                    _isFileDirty = false;
                    labelFileName.Text = String.Format("[{0}]", fastColoredTextBoxFetchXml.Tag);
                }
            }
            catch (Exception ex)
            {
                FillTextBox(ex.ToString());
            }
        }

        private void newToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (_isFileDirty)
            {
                string message = String.Format("Save your changes in file?{0}{0}{1}", Environment.NewLine, fastColoredTextBoxFetchXml.Tag);
                if (MessageBox.Show(message, "Confirmation", MessageBoxButtons.YesNo) == DialogResult.Yes)
                    saveToolStripMenuItem_Click(null, new EventArgs());
            }

            _isFileDirty = false;
            labelFileName.Text = "[]";
            dataGridViewReplacements.CellValueChanged -= dataGridViewReplacements_CellValueChanged;
            fastColoredTextBoxFetchXml.TextChanged -= fastColoredTextBoxFetchXml_TextChanged;

            string newFetch = @"<fetch version='1.0' output-format='xml-platform' mapping='logical' no-lock='true' distinct='false'>
    <entity name=''>
        <attribute name='' />
        <filter type='and'>
            <condition attribute='' operator='eq' value='{{recordId}}' />
        </filter>
    </entity>
</fetch>";

            dataGridViewReplacements.Rows.Clear();
            dataGridViewReplacements.Rows.Add(true, "{recordId}", String.Empty, String.Empty);
            fastColoredTextBoxFetchXml.Tag = null;
            fastColoredTextBoxFetchXml.Text = newFetch;
        }

        private void SaveXmlFile(Stream stream)
        {
            using (XmlTextWriter xmlTextWriter = new XmlTextWriter(stream, Encoding.UTF8))
            {
                xmlTextWriter.Formatting = Formatting.Indented;
                xmlTextWriter.Indentation = 4;

                xmlTextWriter.WriteStartElement("root");

                XmlReader reader = XmlReader.Create(new StringReader(fastColoredTextBoxFetchXml.Text), new XmlReaderSettings() { IgnoreWhitespace = true });
                xmlTextWriter.WriteNode(reader, true);

                xmlTextWriter.WriteStartElement("keyValuePairs");
                string key, value, use, note;
                foreach (DataGridViewRow row in dataGridViewReplacements.Rows)
                {
                    use = row.Cells["Use"].Value != null ? row.Cells["Use"].Value.ToString() : Boolean.FalseString;
                    key = row.Cells["Key"].Value != null ? row.Cells["Key"].Value.ToString() : null;
                    value = row.Cells["Value"].Value != null ? row.Cells["Value"].Value.ToString() : null;
                    note = row.Cells["Note"].Value != null ? row.Cells["Note"].Value.ToString() : null;

                    if (use == Boolean.FalseString && String.IsNullOrWhiteSpace(key) &&
                        String.IsNullOrWhiteSpace(value) && String.IsNullOrWhiteSpace(note))
                        continue;

                    xmlTextWriter.WriteStartElement("keyValuePair");
                    xmlTextWriter.WriteAttributeString("use", use);
                    xmlTextWriter.WriteAttributeString("key", key);
                    xmlTextWriter.WriteAttributeString("value", value);
                    xmlTextWriter.WriteAttributeString("note", note);
                    xmlTextWriter.WriteEndElement();
                }

                xmlTextWriter.WriteEndElement();
                xmlTextWriter.WriteEndElement();
            }
        }

        void dataGridViewReplacements_CellValueChanged(object sender, DataGridViewCellEventArgs e)
        {
            _isFileDirty = true;
            labelFileName.Text = String.Format("[{0}]*", fastColoredTextBoxFetchXml.Tag);
            dataGridViewReplacements.CellValueChanged -= dataGridViewReplacements_CellValueChanged;
            fastColoredTextBoxFetchXml.TextChanged -= fastColoredTextBoxFetchXml_TextChanged;
        }

        void fastColoredTextBoxFetchXml_TextChanged(object sender, FastColoredTextBoxNS.TextChangedEventArgs e)
        {
            _isFileDirty = true;
            labelFileName.Text = String.Format("[{0}]*", fastColoredTextBoxFetchXml.Tag);
            dataGridViewReplacements.CellValueChanged -= dataGridViewReplacements_CellValueChanged;
            fastColoredTextBoxFetchXml.TextChanged -= fastColoredTextBoxFetchXml_TextChanged;
        }

        private void textBoxInfo_TextChanged(object sender, EventArgs e)
        {
            TextBoxScrollToEnd();
        }

        private void textBoxInfo_Layout(object sender, LayoutEventArgs e)
        {
            TextBoxScrollToEnd();
        }

        private void TextBoxScrollToEnd()
        {
            textBoxInfo.SelectionStart = textBoxInfo.Text.Length;
            textBoxInfo.ScrollToCaret();
        }

        private void backgroundWorkerServer_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
        {
            BackgroundWorker backgroundWorker = sender as BackgroundWorker;

            Tuple<string, int, int, string> argument = e.Argument as Tuple<string, int, int, string>;

            string pagingCookie = argument.Item4;
            bool moreRecords;
            int totalRecordCount;

            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();

            QueryExpression query = _organizationService.FetchXmlToQueryExpression(argument.Item1);

            EntityCollection entityCollection = null;

            // pouze pokud není počet záznamů ovlivněn samotným fetch xml, atributem count
            if (query.PageInfo.Count == 0)
            {
                query.PageInfo.Count = argument.Item2;
                query.PageInfo.PageNumber = argument.Item3;
                query.PageInfo.PagingCookie = !String.IsNullOrWhiteSpace(pagingCookie) ? pagingCookie : query.PageInfo.PagingCookie;
                query.PageInfo.ReturnTotalRecordCount = true;
            }

            entityCollection = _organizationService.RetrieveMultiple(query);

            pagingCookie = entityCollection.PagingCookie;
            moreRecords = entityCollection.MoreRecords;
            if (entityCollection.TotalRecordCount == -1 && query.PageInfo.Count > 0)
                totalRecordCount = query.PageInfo.Count;
            else
                totalRecordCount = entityCollection.TotalRecordCount;

            stopwatch.Stop();
            string elapsedTime = String.Format("(in {0} miliseconds)", stopwatch.ElapsedMilliseconds);

            if (backgroundWorker != null)
                backgroundWorker.ReportProgress(20, "creating datasource");

            Thread.Sleep(100);

            DataTable dataTable = new DataTable(query.EntityName);
            List<DataColumn> dataColumns = new List<DataColumn>();

            foreach (Entity entity in entityCollection.Entities)
            {
                foreach (string key in entity.Attributes.Keys)
                {
                    if (!dataColumns.Any(c => c.ColumnName == key))
                    {
                        DataColumn dataColumn = new DataColumn(key, typeof(Object));

                        string entityName = query.EntityName;
                        string attName = key;
                        object value = entity[key];
                        if (value is AliasedValue)
                        {
                            entityName = ((AliasedValue)value).EntityLogicalName;
                            attName = ((AliasedValue)value).AttributeLogicalName;
                        }

                        EntityMetadata entityMetadata = EntityMetadataHelper.GetMetadata(entityName, EntityFilters.Attributes, _organizationService);
                        AttributeMetadata attributeMetadata = entityMetadata.Attributes.FirstOrDefault(am => am.LogicalName == attName);
                        string entityDisplayName = (entityMetadata != null && entityMetadata.DisplayName != null && entityMetadata.DisplayName.UserLocalizedLabel != null && entityMetadata.DisplayName.UserLocalizedLabel.Label != null) ?
                            entityMetadata.DisplayName.UserLocalizedLabel.Label : String.Empty;
                        string attributeDisplayName = (attributeMetadata != null && attributeMetadata.DisplayName != null && attributeMetadata.DisplayName.UserLocalizedLabel != null && attributeMetadata.DisplayName.UserLocalizedLabel.Label != null) ?
                            attributeMetadata.DisplayName.UserLocalizedLabel.Label : String.Empty;

                        dataColumn.ExtendedProperties.Add("columnTooltip", entityDisplayName + "." + attributeDisplayName);
                        dataColumns.Add(dataColumn);
                    }
                }
            }

            dataColumns = dataColumns.OrderBy(col => col.ColumnName, new ColumnComparer(query.EntityName)).ToList<DataColumn>();

            foreach (DataColumn item in dataColumns)
                dataTable.Columns.Add(item);

            if (backgroundWorker != null)
                backgroundWorker.ReportProgress(10, "creating datasource");

            Thread.Sleep(100);

            List<object> values = new List<object>();
            for (int i = 0; i < entityCollection.Entities.Count; i++)
            {
                Entity entity = entityCollection.Entities[i];
                values.Clear();
                foreach (DataColumn dataColumn in dataTable.Columns)
                {
                    if (entity.Contains(dataColumn.ColumnName))
                        values.Add(new Tuple<object, string>(entity[dataColumn.ColumnName], entity.FormattedValues.Contains(dataColumn.ColumnName) ? entity.FormattedValues[dataColumn.ColumnName] : null));
                    else
                        values.Add(null);
                }

                dataTable.Rows.Add(values.ToArray());
            }

            if (backgroundWorker != null)
                backgroundWorker.ReportProgress(10, "filling datagrid");

            Thread.Sleep(100);

            e.Result = new Tuple<DataTable, string, bool, int, string>(dataTable, pagingCookie, moreRecords, totalRecordCount, elapsedTime);
        }

        private void backgroundWorkerServer_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
                FillTextBox(e.Error.ToString());
            else
            {
                Tuple<DataTable, string, bool, int, string> result = e.Result as Tuple<DataTable, string, bool, int, string>;
                if (result != null)
                {
                    _dataTable = result.Item1;
                    _pagingCookie = result.Item2;
                    _moreRecords = result.Item3;
                    _totalRecordCount = result.Item4;
                    elapsedTimeLabel.Text = result.Item5;

                    if (_dataTable.Rows.Count > 0)
                    {
                        dataGridViewResult.DataSource = _dataTable;

                        toolStripProgressBar1.Increment(40);

                        for (int i = 0; i < dataGridViewResult.Rows.Count; i++)
                            dataGridViewResult.Rows[i].HeaderCell.Value = (i + 1).ToString();
                    }
                    else
                        noresultLabel.Visible = true;

                    for (int i = 0; i < dataGridViewResult.Columns.Count; i++)
                        dataGridViewResult.Columns[i].HeaderCell.ToolTipText = (string)_dataTable.Columns[i].ExtendedProperties["columnTooltip"];

                    toolStripProgressBar1.Value = 100;

                    labelPageNumber.Text = String.Format("page {0}", _pageNumber);
                    int pageFirstRecordNumber = ((_pageNumber - 1) * _pageRecordCount) + 1;
                    int pageLastRecordNumber = _pageNumber * _pageRecordCount;
                    string total = _totalRecordCount == 5000 && _moreRecords ? _totalRecordCount.ToString() + "+" : _totalRecordCount.ToString();
                    labelResultCount.Text = String.Format("{0}-{1}/{2}", pageFirstRecordNumber, pageLastRecordNumber, total);
                    labelResultCount.Visible = true;

                    buttonPreviousPage.Enabled = buttonFirstPage.Enabled = _pageNumber > 1;
                    buttonNextPage.Enabled = buttonLastPage.Enabled = _moreRecords;
                }
            }

            buttonEvaluate.Enabled = true;

            toolStripStatusLabel2.Text = "ready";
            toolStripProgressBar1.Value = 0;
        }

        void dataGridViewResult_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
        {
            string displayValue;

            if (e.Value is Tuple<object, string>)
            {
                Tuple<object, string> tuple = (Tuple<object, string>)e.Value;
                object value = tuple.Item1;
                string tooltipValue = tuple.Item2;

                string entityName = null;
                value = GetDisplayValue(value, ref entityName, ref tooltipValue);

                DataGridView dataGridViewResult = sender as DataGridView;

                if (value is Guid)
                {
                    if (dataGridViewResult != null && String.IsNullOrEmpty(entityName))
                    {
                        DataTable dataTable = dataGridViewResult.DataSource as DataTable;
                        if (dataTable != null && dataGridViewResult.Columns[e.ColumnIndex].Name.StartsWith(dataTable.TableName))
                            entityName = dataTable.TableName;
                    }

                    Guid guid = (Guid)value;
                    displayValue = guid.ToString();

                    if (dataGridViewResult != null && !String.IsNullOrEmpty(entityName))
                    {
                        e.CellStyle.ForeColor = Color.Blue;
                        e.CellStyle.Font = new Font(e.CellStyle.Font, FontStyle.Underline);

                        dataGridViewResult.Rows[e.RowIndex].Cells[e.ColumnIndex].Tag = GetUrlFromRecord(entityName, guid.ToString());
                    }
                }
                else
                    displayValue = value.ToString();

                if (dataGridViewResult != null && !String.IsNullOrEmpty(tooltipValue))
                    dataGridViewResult.Rows[e.RowIndex].Cells[e.ColumnIndex].ToolTipText = tooltipValue;
            }
            else
                displayValue = e.Value.ToString();

            e.Value = displayValue;
            e.FormattingApplied = true;
        }

        void dataGridViewResult_CellContentDoubleClick(object sender, DataGridViewCellEventArgs e)
        {
            if (e.RowIndex == -1)
                return;

            string tag = dataGridViewResult.Rows[e.RowIndex].Cells[e.ColumnIndex].Tag as string;
            if (!String.IsNullOrEmpty(tag))
            {
                try
                {
                    System.Diagnostics.Process.Start(tag);
                }
                catch (Exception ex)
                {
                    FillTextBox(ex.ToString());
                }
            }
        }

        private object GetDisplayValue(object value, ref string entityName, ref string tooltipValue)
        {
            if (value is EntityReference)
            {
                entityName = ((EntityReference)value).LogicalName;
                tooltipValue = ((EntityReference)value).Name;
                return ((EntityReference)value).Id;
            }
            else if (value is OptionSetValue)
                return ((OptionSetValue)value).Value;
            else if (value is Money)
                return ((Money)value).Value;
            else if (value is AliasedValue)
            {
                entityName = ((AliasedValue)value).EntityLogicalName;
                return GetDisplayValue(((AliasedValue)value).Value, ref entityName, ref tooltipValue);
            }
            else
                return value;
        }

        private string GetUrlFromRecord(string entityLogicalName, string entityId)
        {
            // http://<mycrm>/<myOrg>/main.aspx?pagetype=entityrecord&etn=<entityLogicalName>&id=<entityId>
            return String.Format("{0}/main.aspx?pagetype=entityrecord&etn={1}&id={2}", _connection.ServiceUri, entityLogicalName, entityId);
        }

        private void comboBoxConnectionString_SelectionChangeCommitted(object sender, EventArgs e)
        {
            string connectionString = ((ComboBox)sender).SelectedValue.ToString();
            toolStripStatusLabel1.Text = connectionString;
            try
            {
                Initialize(connectionString);
            }
            catch (Exception ex)
            {
                FillTextBox(ex.ToString());
            }
        }

        private void backgroundWorkerServer_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            if (e.UserState != null)
                toolStripStatusLabel2.Text = e.UserState.ToString();

            toolStripProgressBar1.Increment(e.ProgressPercentage);
        }

        private void FetchXmlEvaluator_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.F5)
            {
                buttonEvaluate_Click(sender, e);
                e.Handled = true;
            }
            else if (e.Control && e.KeyCode == Keys.S)
            {
                if (fastColoredTextBoxFetchXml.Tag == null)
                    saveToolStripMenuItem_Click(null, new EventArgs());
                else
                {
                    string path = fastColoredTextBoxFetchXml.Tag.ToString();

                    if (String.IsNullOrWhiteSpace(path))
                        return;

                    using (Stream stream = new FileStream(path, FileMode.Create, FileAccess.ReadWrite))
                        SaveXmlFile(stream);

                    fastColoredTextBoxFetchXml.TextChanged += fastColoredTextBoxFetchXml_TextChanged;
                    dataGridViewReplacements.CellValueChanged += dataGridViewReplacements_CellValueChanged;
                    _isFileDirty = false;
                    labelFileName.Text = String.Format("[{0}]", fastColoredTextBoxFetchXml.Tag);
                }
            }
        }

        internal class ColumnComparer : IComparer<string>
        {
            StringComparer _stringComparer;
            private string _entityName;
            private string _entityNameId;

            public ColumnComparer(string entityName)
            {
                _entityName = entityName;
                _entityNameId = entityName + "id";

                _stringComparer = StringComparer.Create(CultureInfo.InvariantCulture, true);
            }

            public int Compare(string x, string y)
            {
                if (x.StartsWith(_entityNameId, StringComparison.InvariantCultureIgnoreCase) && !y.StartsWith(_entityNameId, StringComparison.InvariantCultureIgnoreCase))
                    return -1;
                else if (!x.StartsWith(_entityNameId, StringComparison.InvariantCultureIgnoreCase) && y.StartsWith(_entityNameId, StringComparison.InvariantCultureIgnoreCase))
                    return 1;
                else if (!x.Contains(".") && y.Contains("."))
                    return -1;
                else if (x.Contains(".") && !y.Contains("."))
                    return 1;
                else if (x.StartsWith(_entityName, StringComparison.InvariantCultureIgnoreCase) && !y.StartsWith(_entityName, StringComparison.InvariantCultureIgnoreCase))
                    return -1;
                else if (!x.StartsWith(_entityName, StringComparison.InvariantCultureIgnoreCase) && y.StartsWith(_entityName, StringComparison.InvariantCultureIgnoreCase))
                    return 1;
                else
                    return _stringComparer.Compare(x, y);
            }
        }

        private void buttonConnectionManager_Click(object sender, EventArgs e)
        {
            connectionsForm.ShowDialog(this);
        }
    }
}
